package com.drumbeatinsight.managers
{
	import com.drumbeatinsight.controls.HTML;
	import com.drumbeatinsight.html.HTMLDivision;
	
	import flash.events.EventDispatcher;
	import flash.external.ExternalInterface;
	import flash.utils.Timer;
	
	import mx.collections.ArrayCollection;
	import mx.core.Application;

	/**
	 *  Class is a Singleton
	 * hide all html windows
	 * show all html windows
	 * hide visible html windows
	 * restore visible html windows
	 */
	public class HTMLManager extends EventDispatcher {
		
		private static var instance:HTMLManager;
		private static var created:Boolean;
		public var addedCallbacks:Boolean = false;
		public static var pageLoaded:Boolean = false;
		public static var pageLoadedTimer:Timer = new Timer(250);
		private static var _items:Array = new Array();
		public static var items:ArrayCollection = new ArrayCollection(_items);
		public var promptOnUnload:Boolean = false;
		// this message is shown in between the default message the browsers displays
		// Are you sure you want to navigate away from this page?
		// Press OK to continue, or Cancel to stay on the current page.
		public var promptOnUnloadMessage:String = "";
		public static var addedAlertListeners:Boolean = false;
		
		public static const PROMPT_ON_UNLOAD:String = "fcPromptOnUnload";
		
		public static var OK:String = "OK";
		public static var CANCEL:String = "Cancel";
		public static var YES:String = "Yes";
		public static var NO:String = "No";
		
		public static const HTML_ALERT_HANDLER:String = "dispatchAlertHandler";
		public static const HTML_CREATION_COMPLETE:String = "dispatchCreationComplete";
		public static const HTML_PAGE_CREATION_COMPLETE:String = "dispatchPageCreationComplete";
		public static const HTML_UNLOAD:String = "dispatchHTMLUnload";
		public static const HTML_POP_UP_CONTENT_LOADED:String = "dispatchPopUpContentLoaded";
		public static const HTML_POP_UP_FOCUS:String = "dispatchPopUpFocus";
		public static const HTML_POP_UP_MINIMIZE:String = "dispatchPopUpMinimize";
		public static const HTML_POP_UP_MAXIMIZE:String = "dispatchPopUpMaximize";
		public static const HTML_POP_UP_RESTORE:String = "dispatchPopUpRestore";
		public static const HTML_POP_UP_RESIZE:String = "dispatchPopUpResize";
		public static const HTML_POP_UP_CLOSE:String = "dispatchPopUpClose";
		public static const HTML_POP_UP_CLOSE_COMPLETE:String = "dispatchPopUpCloseComplete";
		
		// html pop up window methods
		public static const CASCADE_WINDOWS:String = "fcCascadeWindows";
		public static const CLOSE_WINDOW:String = "fcCloseWindow";
		public static const CLOSE_WINDOWS:String = "fcCloseWindows";
		public static const FOCUS_WINDOW:String = "fcFocusWindow";
		public static const MAXIMIZE_WINDOW:String = "fcMaximizeWindow";
		public static const MAXIMIZE_WINDOWS:String = "fcMaximizeWindows";
		public static const MINIMIZE_WINDOW:String = "fcMinimizeWindow";
		public static const MINIMIZE_WINDOWS:String = "fcMinimizeWindows";
		public static const RESTORE_MINIMIZED_WINDOW:String = "fcRestoreMinimizedWindow";
		public static const RESTORE_WINDOW:String = "fcRestoreWindow";
		public static const TOGGLE_WINDOW_DOCK:String = "fcToggleDock";
		public static const SET_WINDOW_DOCK_VISIBILITY:String = "fcSetDockVisibility";
		
		/**
		 * Constructor - Singleton
		 */
		public function HTMLManager() {
			if (!created) {
				throw new Error("HTMLManager class cannot be instantiated");
			}
		}
		
		/**
		 * Returns the one single instance of this class
		 */
		public static function getInstance():HTMLManager {
			if (instance == null) {
				created = true;
				instance = new HTMLManager();
				created = false;
			}
			
			return instance;
		}
		
		/**
		 * Dispatches a creation complete event for html elements
		 */
		public static function onHTMLAlertHandler(id:String, buttonValue:String = ""):void {
			dispatchCustomEvent(HTML_ALERT_HANDLER, id, buttonValue);
		}
		
		/**
		 * Dispatches a creation complete event for html elements
		 */
		public static function onHTMLCreationComplete(id:String):void {
			dispatchCustomEvent(HTML_CREATION_COMPLETE, id);
		}
		
		/**
		 * Dispatches a HTML window minimize event
		 */
		public static function onHTMLPopUpMinimize(id:String):void {
			dispatchCustomEvent(HTML_POP_UP_MINIMIZE, id);
		}
		
		/**
		 * Dispatches a HTML window maximize event
		 */
		public static function onHTMLPopUpMaximize(id:String):void {
			dispatchCustomEvent(HTML_POP_UP_MAXIMIZE, id);
		}
		
		/**
		 * Dispatches a HTML window restore event
		 */
		public static function onHTMLPopUpRestore(id:String):void {
			dispatchCustomEvent(HTML_POP_UP_RESTORE, id);
		}
		
		/**
		 * Dispatches a HTML window close event
		 */
		public static function onHTMLPopUpClose(id:String):void {
			dispatchCustomEvent(HTML_POP_UP_CLOSE, id);
		}
		
		/**
		 * Dispatches a HTML window close complete event
		 */
		public static function onHTMLPopUpCloseComplete(id:String):void {
			dispatchCustomEvent(HTML_POP_UP_CLOSE_COMPLETE, id);
		}
		
		/**
		 * Dispatches a HTML window focus event
		 */
		public static function onHTMLPopUpFocus(id:String):void {
			dispatchCustomEvent(HTML_POP_UP_FOCUS, id);
		}
		
		/**
		 * Dispatches a HTML window resize event
		 */
		public static function onHTMLPopUpResize(id:String):void {
			dispatchCustomEvent(HTML_POP_UP_RESIZE, id);
		}
		
		/**
		 * Dispatches a HTML window content loaded event
		 */
		public static function onHTMLPopUpContentLoaded(id:String):void {
			dispatchCustomEvent(HTML_POP_UP_CONTENT_LOADED, id);
		}
		
		/**
		 * Dispatches a creation complete event when page loads. Not used atm
		 */
		public static function onNavigateAwayCancel(id:String):void {
			dispatchCustomEvent(HTML_POP_UP_CLOSE, id);
		}
		
		/**
		 * Dispatches a creation complete event when page loads. Not used atm
		 */
		public static function onPageCreationComplete(id:String):void {
			dispatchCustomEvent(HTML_PAGE_CREATION_COMPLETE, id);
		}
		
		/**
		 * Dispatches a creation complete event when page loads. Not used atm
		 */
		public static function onHTMLUnload(id:String):void {
			dispatchCustomEvent(HTML_PAGE_CREATION_COMPLETE, id);
		}
		
		/**
		 * Dispatches the specified event / method on a specific component
		 */
		public static function dispatchCustomEvent(methodName:String, id:String, ... additionalParameters):void {
			var len:int = items.length;
			for (var i:int=0;i<len;i++) {
				var item:HTML = items.getItemAt(i) as HTML;
				if (item.id == id) {
					if (additionalParameters.length > 0) {
						item[methodName](additionalParameters);
					}
					else {
						item[methodName]();
					}
				}
			}
		}
		
		// shows an alert
		// text - text or html to display
		// title - alert title
		// flags - an array of strings displayed to the user. required. [HTMLManager.OK, "CANCEL", "etc"]
		// parent - is the component or container. not required
		// closeHandler - function that is called when the user clicks any button. the button clicked is in HTML(event.currentTarget).alertButtonValue;
		// iconClass - not implemented
		// defaultButtonFlag - not implemented
		public static function alert(text:String, title:String, flags:Array, parent:* = null, 
			closeHandler:Function = null, iconClass:Class = null, defaultButtonFlag:String = null) :void {
			
			var html:HTML = new HTML();
			html.popUpTitle = title;
			html.popUpChrome = true;
			html.htmlText = text;
			html.popUpModal = true;
			html.popUpAlert = true;
			html.popUpAlertButtons = flags as Array;
			html.popUpCloseable = false;
			//html.popUpLaunchCentered = true; // does this work???
			html.elementType = "division";
			html.includeInLayout = false;
			
			// this is all to position the component in the correct place when application scrollbars are present
			// we should change this
			// UPDATE: centering may not work because the window the chrome is not added into the window size. 
			// it may not be including the chrome in the dimensions when we create the window
			// see notes in mocha.js 
			
			var h1:com.drumbeatinsight.html.HTMLDivision; // we do a check in this class for pop ups
			var appHeight:Number = Application.application.height;
			var appWidth:Number = Application.application.width;
			var height:Number = uint(appHeight / 3);
			var width:Number = uint(appWidth / 3);
			var heightDifference:Number = uint(appHeight - height);
			var widthDifference:Number = uint(appWidth - width);
			var y:Number = uint(heightDifference / 2);
			var x:Number = uint((appWidth - width) / 2);
			html.height = height;
			html.width = width;
			html.y = (y < 0) ? 0 : y;
			html.x = x;
			
			if (closeHandler!=null) {
				html.addEventListener("htmlAlertHandler", closeHandler, false, 0, true);
			}
			
			if (parent!=null) {
				parent.addChild(html);
			}
			else {
				Application.application.addChild(html);
			}
			
		}
		
		// add html element to html items array
		// this is automatically called when you create a new HTML instance
		// you should not need to ever call this
		public static function add(html:HTML) :void {
			if (html.id==null) {
				//trace("html id =", html.id);
				html.id = html.name + items.length;
				//trace("html id2 =", html.id);
			}
			items.addItem(html);
		}
		
		// remove html element from html manager items array
		// this is automatically called when you remove a HTML instance 
		// using removeElement() or htmlInstance.parent.removeChild(htmlInstance)
		// you should not need to ever call this
		public static function removeFromItems(html:HTML) :void {
			var item:HTML;
			
			for (var i:int=0; i<items.length;i++) {
				item = items[i];
				if (item==null) { items.removeItemAt(i); }
				if (item != null && item.hasOwnProperty("id") && html.id==item.id) {
					items.removeItemAt(i);
				}
			}
		}
		
		// remove html element 
		public static function remove(html:HTML=null) :void {
			
			if (html!=null) {
				html.removeElement();
			}
		}
		
		// return html component instance from html manager items array
		public static function getAllComponents():Array {
			return items.source;
		}
		
		public static function getComponentById(id:String):HTML {
			for each (var item:HTML in items) {
				if (item.id == id) {
					return item;
				}
			}
			return null;
		}
		
		private static var _visibleItems:Array = [];
		private static var _visibleItemsHidden:Boolean = false;
		private static var _setVisible:Boolean = false;
		
		/**
		 * Hide all visible elements via the htmlVisibility property.
		 * This method is to temporarily hide the elements
		 * @setVisibleProperty - parameter to set the visible property instead of the htmlVisibility
		 * */
		public static function hideVisibleElements(setVisibleProperty:Boolean = true):void {
			var len:int = items.length;
			var item:HTML;
			var isVisible:Boolean = false;
			var display:String = "";
			var id:String = "";
			
			// prevent the list of visible items from getting overwritten if hide is called twice
			if (!_visibleItemsHidden) {
				_visibleItems = new Array();
				_visibleItems.length = 0;
				_visibleItemsHidden = true;
			}
			
			_setVisible = setVisibleProperty;
			
			for each (item in items) {
				id = item.id;
				// we can't check the visible property because html pop up windows hide the underlying textarea
				// so we check if the instance is visible in the browser dom
				display = ExternalInterface.call("eval", "fcGetElement('" + item.id + "').style.display");
				
				// we should remove this element from the items list
				if (display == null) {
					remove(item);
					continue;
				}
				
				// if display is block than it's visible
				if (display.toLowerCase()=="block") {
					_visibleItems.push(item);
					if (setVisibleProperty) {
						// htmlVisibility needs to be first
						item.htmlVisibility = false;
						item.visible = false;
					}
					else {
						item.htmlVisibility = false;
					}
				}
			}
			
		}
			
		/**
		 * Show all elements hidden with the hideVisibleElements method.
		 * Restores their visibility the way the element was hidden. 
		 * In other words by setting either the htmlVisibility or visible property.
		 * */
		public static function showVisibleElements():void {
			var len:int = items.length;
			var item:HTML;
			var visibleItem:HTML;
			
			for each (item in items) {
				for each (visibleItem in _visibleItems) {
					if (item.id == visibleItem.id) {
						if (_setVisible) {
							item.visible = true;
							item.htmlVisibility = true;
						}
						else {
							item.htmlVisibility = true;
						}
					}	
				}
			}
			
			_visibleItems.length = 0;
		}
		
		/**
		* Prompts the user with a javascript alert if they try to navigate away from the page
		*/
		public static function enablePromptOnUnload(value:Boolean):void {
			instance.promptOnUnload = value;
			ExternalInterface.call(PROMPT_ON_UNLOAD, value, instance.promptOnUnloadMessage);
		}


	    /**
		* Cascade html pop up windows
		*/
	    public static function cascadeWindows():void {
			ExternalInterface.call(CASCADE_WINDOWS);
	    }

	    /**
		* Close html pop up window
		*/
	    public static function closeWindow(id:String):void {
			ExternalInterface.call(CLOSE_WINDOW, id);
	    }

	    /**
		* Close html pop up windows
		*/
	    public static function closeWindows():void {
			ExternalInterface.call(CLOSE_WINDOWS);
	    }

	    /**
		* Focus html pop up window
		*/
	    public static function focusWindow(id:String):void {
			ExternalInterface.call(FOCUS_WINDOW, id);
	    }

	    /**
		* Maximize html pop up window
		*/
	    public static function maximizeWindow(id:String):void {
			ExternalInterface.call(MAXIMIZE_WINDOW, id);
	    }

	    /**
		* Minimize html pop up window
		*/
	    public static function minimizeWindow(id:String):void {
			ExternalInterface.call(MINIMIZE_WINDOW, id);
	    }

	    /**
		* Minimize html pop up windows
		*/
	    public static function minimizeWindows():void {
			ExternalInterface.call(MINIMIZE_WINDOWS);
	    }

	    /**
		* Restore minimized html pop up window
		*/
	    public static function restoreMinimizedWindow(id:String):void {
			ExternalInterface.call(RESTORE_MINIMIZED_WINDOW, id);
	    }

	    /**
		* Restore html pop up window
		*/
	    public static function restoreWindow(id:String):void {
			ExternalInterface.call(RESTORE_WINDOW, id);
	    }

	    /**
		* Hide or unhide the html pop up window dock
		*/
	    public static function toggleWindowDock():void {
			ExternalInterface.call(TOGGLE_WINDOW_DOCK);
	    }

	    /**
		* Sets the visibility of the pop up window dock
		*/
	    public static function setWindowDockVisibility(value:Boolean):void {
			ExternalInterface.call(SET_WINDOW_DOCK_VISIBILITY, value);
	    }
	    
	    private var _windowDockVisibility:Boolean = false;
	    public function get windowDockVisibility():Boolean {
			return _windowDockVisibility;
	    }

	    /**
		* Sets the visibility of the pop up window dock
		*/
	    public function set windowDockVisibility(value:Boolean):void {
	    	_windowDockVisibility = value;
			ExternalInterface.call(SET_WINDOW_DOCK_VISIBILITY, value);
	    }
	}
}